/*
 * Copyright © 2009 Gerald Isaac Schwarz
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package base;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;

public class Board {

	private Player currentPlayer;
	private Rectangle size;
	private ArrayList<Piece> pieces;
	private ArrayList<Player> players;
	private Square[][] squares;
	private Player last;
	
	public Board(Player currentPlayer, Dimension size, ArrayList<Piece> pieces) {
		this.currentPlayer = currentPlayer;
		this.pieces = new ArrayList<Piece>(pieces);
		
		this.setSize(size);
		
		this.refreshPlayers();
		this.refreshLastPlayer();
	}
	
	public void setSize(Dimension size) {
		this.size = new Rectangle(size);
		this.squares = new Square[this.getSize().height][this.getSize().width];
		
		for (int y = 0; y < this.getSize().height; y++) {
			for (int x = 0; x < this.getSize().width; x++) {
				Color color;
				
				if ((y % 2 == 0 && x % 2 == 0) || (y % 2 == 1 && x % 2 == 1)) {
					color = Color.BLACK;
				}
				else {
					color = Color.WHITE;
				}
				
				Square square = new Square(new Point(x, y), color);
				this.squares[y][x] = square;
			}
		}
		
		for (Piece piece : this.getPieces()) {
			State state = this.getCurrentState(piece);
			
			if (this.contains(state.getLocation())) {
				this.setLocation(piece, state.getLocation());
			}
			else {
				this.remove(piece);
			}
		}
	}
	
	/**
	 * Get the player who's turn it is to make a move.
	 * @return The player who's turn it is to make a move.
	 */
	public Player getCurrentPlayer() {
		return this.currentPlayer;
	}
	
	/**
	 * Set the player who's turn it is to make a move.
	 * @param player The player who's turn it is to make a move.
	 */
	public void setCurrentPlayer(Player player) {
		if (this.hasPlayer(player)) {
			this.currentPlayer = player;
		}
	}
	
	public boolean hasPlayer(Player player) {
		for (Player player2 : this.players) {
			if (player2 == player) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Get all the players who are participating in the game.
	 * @return An ArrayList of the players who are participating in the game.
	 */
	public ArrayList<Player> getPlayers() {
		return this.players;
	}
	
	private void refreshPlayers() {
		this.players = new ArrayList<Player>();
		
		for (Piece piece : this.getPieces()) {
			Player player = this.getCurrentState(piece).getOwner();
			
			if (!this.hasPlayer(player)) {
				this.players.add(player);
			}
		}
	}
	
	public static int nextAvailableId(ArrayList<Player> players) {
		Player last = players.get(0);
		
		for (Player player : players) {
			if (player.getId() > last.getId()) {
				last = player;
			}
		}
		
		return last.getId() + 1;
	}
	
	public Player getPlayerById(int id) {
		for (Player player : this.getPlayers()) {
			if (player.getId() == id) {
				return player;
			}
		}
		
		return null;
	}
	
	public boolean isCurrentPlayerLast() {
		 return this.last == this.getCurrentPlayer();
	}
	
	public Player getLastPlayer() {
		return this.last;
	}
	
	private void refreshLastPlayer() {
		this.last = this.getPlayers().get(0);
		
		for (Player player : this.getPlayers()) {
			if (player.getId() > this.last.getId()) {
				this.last = player;
			}
		}
	}
	
	/**
	 * Advance to the next player.
	 * @return The player who is now the current player.
	 */
	public Player getNextPlayer() {
		if (this.isCurrentPlayerLast()) {
			int lowestId = this.getPlayers().get(0).getId();
			
			for (Player player : this.getPlayers()) {
				if (player.getId() < lowestId) {
					lowestId = player.getId();
				}
			}
			
			return this.getPlayerById(lowestId);
		}
		else {
			int lowestId = this.getCurrentPlayer().getId();
			int highestId = this.getLastPlayer().getId();
			
			for (Player player : this.getPlayers()) {
				if (lowestId  < player.getId()&& player.getId() < highestId) {
					highestId = player.getId();
				}
			}
			
			return this.getPlayerById(highestId);
		}
	}
	
	/**
	 * Get all the pieces in the game.
	 * @return An ArrayList of all the pieces in the game.
	 */
	public ArrayList<Piece> getPieces() {
		return new ArrayList<Piece>(this.pieces);
	}
	
	/**
	 * Get the piece located at the specified coordinates.
	 * @param location The location of the piece which is to be retrieved.
	 * @return The piece that is located at the specified location. Null if there is no piece there or if it's not alive.
	 */
	public Piece getPiece(Point location) {
		return this.squares[location.y][location.x].getPiece();
	}
	
	public State getCurrentState(Piece piece) {
		return new State(piece.getLocation(), piece.getLevel(), piece.getOwner());
	}
	
	/**
	 * Checks whether or not the specified location has a living piece in it.
	 * @param location The location which is to be examined.
	 * @return True if the specified location is empty, false if there is a living piece there.
	 */
	public boolean isEmpty(Point location) {
		return this.squares[location.y][location.x].getPiece() == null;
	}
	
	/**
	 * Get the height and width of the board.
	 * @return A Dimension which stores the number of rows and columns in the board.
	 */
	public Dimension getSize() {
		return this.size.getSize();
	}
	
	/**
	 * Tells you whether or not the given location is inside the boundaries of the board.
	 * @param location The location which is to be examined.
	 * @return True if the location is within the board, false if it's outside the boundaries.
	 */
	public boolean contains(Point location) {
		if (location != null) {
			return this.size.contains(location);
		}
		else {
			return false;
		}
	}

	public void setLevel(Piece piece, int level) {
		piece.setLevel(level);
	}

	public void setLocation(Piece piece, Point destination) {
		if (this.contains(piece.getLocation()) && this.contains(destination)) {
			this.squares[piece.getLocation().y][piece.getLocation().x].setPiece(null);
		}
		
		if (this.contains(destination)) {
			this.squares[destination.y][destination.x].setPiece(piece);
			piece.setLocation(destination);
		}
	}

	
	public void setOwner(Piece piece, Player owner) {
		piece.setOwner(owner);
		
		this.refreshPlayers();
		this.refreshLastPlayer();
	}
	
	public void remove(Piece piece) {
		if (this.contains(piece.getLocation())) {
			this.squares[piece.getLocation().y][piece.getLocation().x].setPiece(null);
		}
		this.pieces.remove(piece);
		
		this.refreshPlayers();
		this.refreshLastPlayer();
	}
	
	public Square getSquare(Point location) {
		return this.squares[location.y][location.x];
	}
	
	public void addPiece(Piece piece) {
		Point location = piece.getLocation();
		
		if (!this.isEmpty(location)) {
			this.remove(this.getPiece(location));
		}
		
		this.pieces.add(piece);
		this.squares[location.y][location.x].setPiece(piece);
		
		this.refreshPlayers();
		this.refreshLastPlayer();
	}

	public void setPieces(ArrayList<Piece> pieces) {
		this.pieces = new ArrayList<Piece>(pieces);
		this.setSize(this.getSize());
		
		this.refreshPlayers();
		this.refreshLastPlayer();
	}
}